package com.shuwen.gcdj.common.aop;

import com.shuwen.gcdj.common.exception.ApiException;
import com.shuwen.gcdj.common.annotation.JWTAuth;
import com.shuwen.gcdj.common.constant.Constants;
import com.shuwen.gcdj.common.constant.MemberCst;
import com.shuwen.gcdj.common.enums.AuthType;
import com.shuwen.gcdj.common.enums.ErrorCode;
import com.shuwen.gcdj.common.error.ErrorReport;
import com.shuwen.gcdj.common.util.crypt.JWTUtil;
import com.shuwen.gcdj.service.auth.AuthService;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.Objects;

@Slf4j
@Aspect
@Component
public class AuthAop {
    private final AuthService authService;

    @Value("${powers.skipAuth}")
    private boolean skipAuth;

    public AuthAop(AuthService authService) {
        this.authService = authService;
    }

    /**
     * controllerMethodCall
     */
    @Pointcut("execution(public * com.shuwen.gcdj.controller..*.*(..))")
    public void controllerMethodCall() {
        //empty
    }

    /**
     *
     * @param joinPoint JoinPoint
     */
    @Before(value = "controllerMethodCall()")
    public void authorize(JoinPoint joinPoint) {
        Method method = getControllerMethod(joinPoint);
        ServletRequestAttributes attributes = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes());
        if (attributes == null) {
            return;
        }
        HttpServletRequest request = attributes.getRequest();

        String pagePowerCode = request.getHeader(MemberCst.HEADER_KEY_POWER_CODE);
        pagePowerCode = pagePowerCode == null || pagePowerCode.length() == 0 ? "" : pagePowerCode;
        request.setAttribute(Constants.REQUEST_KEY_POWER_CODE, pagePowerCode);

        verifyAuth(request, method);
    }

    /**
     * 身份校验
     *
     * @param request HttpServletRequest
     * @param method  Method
     */
    private void verifyAuth(HttpServletRequest request, Method method) {

        JWTAuth auth = method.getAnnotation(JWTAuth.class);
        if (Objects.nonNull(auth)) {
            verifyJwtAuth(request, auth);
        }
    }

    private void verifyJwtAuth(HttpServletRequest request, JWTAuth auth) {
        if (!auth.type().equals(AuthType.JWT)) {
            log.error("AuthAspect.verifyJwtAuth() error: 暂不支持认证方式: {}", auth.type().getValue());
            throw new ApiException(ErrorCode.AUTH_TYPE_UNSUPPORTED);
        }

        String jwtToken = request.getHeader(MemberCst.JWT_TOKEN_HEADER_KEY);
        ErrorCode authResult = authService.authJwt(request, jwtToken, auth, skipAuth);
        if (ErrorReport.hasError(authResult)) {
            log.error("AuthAspect.verifyJwtAuth() authService.authJwt() jwt认证失败. error: {}", authResult);
            MemberCst.LOGIN_USER_ID="Id";
            throw new ApiException(authResult);
        }
        MemberCst.LOGIN_USER_ID= JWTUtil.getUserId(jwtToken);
        request.setAttribute(MemberCst.JWT_TOKEN_HEADER_KEY, jwtToken);
    }

    private Method getControllerMethod(JoinPoint joinPoint) {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        return signature.getMethod();
    }
}
